<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Framework_DB
 *
 * @author      Ian Szewczyk <roamzero@gmail.com>
 * @copyright   (c) 2007 Ian Szewczyk. All rights reserved.
 * @license     http://www.opensource.org/licenses/bsd-license.php
 * @package Framework
 * @filesource
 */

//require_once 'DB.php';

require_once 'MDB2.php';




/**
 * Framework_DB
 *
 * The purpose of this class is mainly as a factory to load DB_Table_Database
 * classes.
 *
 *
 * @author Ian Szewczyk <roamzero@gmail.com>
 * @see http://pear.php.net/package/DB_Table
 * @package Framework
 *
 */
abstract class Framework_DB
{

	/**
	 * $databases
	 *
	 * @access 		private
	 * @var 		array 		$databases
	 * @static
	 */
	static private $databases = array();

	/**
	 * $tables
	 *
	 * @access 		private
	 * @var 		array 		$tables
	 * @static
	 */
	static private $tables = array();
	
	/**
	 * $dbCache
	 *
	 * @access 		private
	 * @var 		array 		$dbCache
	 * @static
	 */
	static private $dbCache = array();
	
	/**
	 * $tableCache
	 *
	 * @access 		private
	 * @var 		array 		$tableCache 
	 * @static
	 */
	static private $tableCache = array();


	/**
	 * factory
	 *
	 * @access 		public
	 * @param 		string 		$databaseModel 
	 * @param 		string 		$module 	
	 * @return 		mixed 		Instance of Framework_DB, PEAR_Error
	 * @static
	 */
	static public function dbFactory($databaseModel, $module = null)
	{
		
		
		if (!array_key_exists($databaseModel, self::$databases)) {
			
			// Check cache
			$cache = Framework_Cache::factory();
			
			if ($db = $cache->get($databaseModel,'DB')) {
					self::$databases[$databaseModel] = $db;
					self::$dbCache[] = $databaseModel;
					return $db;
			}
		
			if ($module) {
				$file = 'Framework/Module/'.$module.'/DB/'.$databaseModel.'.php';
			} else {
				$file = 'Framework/DB/'.$databaseModel.'.php';
			}
			if (!include_once($file)) {
				return PEAR::raiseError('Could not load database file: '.$file);
			}

			$class = 'Framework_DB_'.$databaseModel;
			if (!class_exists($class)) {
				return PEAR::raiseError('Could not find database class: '.$class);
			}

			try {
				$instance = new $class($databaseModel, $module);
			
				if (!$instance instanceof Framework_DB_Common) {
					return PEAR::raiseError('Database class must extend from Framework_DB_Common');
				}
			} catch (Framework_Exception $error) {
				return PEAR::raiseError($error->getMessage());
			}
		
			self::$databases[$databaseModel] = $instance;
			return $instance;
		} else {
			return self::$databases[$databaseModel];
		}
			
			
			
	}

	/**
	 * tableFactory
	 *
	 * @access 		public
	 * @param 		string 		$table 		Name of table to load
	 * @param 		object 		$db 		PEAR::db connection object
	 * @param 		string 		$module 
	 * @return 		mixed 		Instance of Framework_DB_Table_Common, PEAR_Error
	 * @static
	 */
	static public function tableFactory($table, $db = null, $module = null)
	{
		if (!array_key_exists($table, self::$tables)) {
			
			// Check cache
			$cache = Framework_Cache::factory();
			
			if ($cachedTable = $cache->get($table,'Table')) {
					self::$tables[$table] = $cachedTable;
					self::$tableCache[] = $table;
					return $cachedTable;
			}
			
			
			
			if ($module) {
				$file = 'Framework/Module/'.$module.'/DB/Table/'.$table.'.php';
			} else {
				$file = 'Framework/DB/Table/'.$table.'.php';
			}
			
			if (!include_once($file)) {
				return PEAR::raiseError('Could not load table file: '.$file);
			}

			$class = 'Framework_DB_Table_'.$table;
			if (!class_exists($class)) {
				return PEAR::raiseError('Could not find table class: '.$class);
			}

			try {
				if (!$db) {
					$instance = new $class(self::getDBConnection());
				} else {
					$instance = new $class($db);
				}
				if (!$instance instanceof Framework_DB_Table_Common) {
					return PEAR::raiseError('Table class must extend from Framework_DB_Common');
				}
			} catch (Framework_Exception $error) {
				return PEAR::raiseError($error->getMessage());
			}
			
			self::$tables[$table] = $instance;
			return $instance;
		} else {
			return self::$tables[$table];
		}
	}

	/**
     * dataGridFactory
     *
     * @access 		public
     * @param		object								$dbObject
     * @param		array								$query
     * @param		integer								$pageSize
     * @param		string								$prefix
     * @param		Framework_Cache_Handler_Common		$cacheHandler
     * @static
     * @return 		mixed								Framework_DB_Datagrid on success, PEAR::error on failure		
     */
	static public function dataGridFactory($dbObject, $query, $pageSize = 10, $prefix = 'page', $cacheHandler = null)
	{
		if ($dbObject instanceof Framework_DB_Common ||
		$dbObject instanceof Framework_DB_Table_Common) {

			try {
				$dataGrid = new Framework_DB_Datagrid($dbObject, $query, $pageSize, $prefix, $cacheHandler);
			} catch (Framework_Exception $error) {
				return PEAR::raiseError($error->getMessage());
			}
				
			return $dataGrid;

				
		} else {
				
			return PEAR::raiseError('Invalid Database Object');
				
		}


	}

	/**
	 * getDBConnection
	 *
	 * @access  public
	 * @return  object
	 * @static
	 */
	static public function getDBConnection()
	{
			
		if (!($dsn = Framework::$site->config('Framework', 'DB'))) {
			Framework::$db = null;
		} else {
			if (!Framework::$db) {
				$options = array(
					'result_buffering' => false,
					'portability' => MDB2_PORTABILITY_NONE
				);
			    Framework::$db = MDB2::factory($dsn, $options);
				// Need this for DB_Table
			    Framework::$db->loadModule('Extended');
			   
			    // Default fetch mode is associative array
			    Framework::$db->setFetchMode(MDB2_FETCHMODE_ASSOC);
			}
		}


		return Framework::$db;
	}

	/**
	 * logDependency
	 *
	 * @access  public
	 * @param   Framework_Cache_Handler_Common		$handler
	 * @param	string								$table
	 * @return  void
	 * @static
	 */
	static public function logDependency(Framework_Cache_Handler_Common $handler, $table)
	{
		
		
		// Using the base 'Framework' cache
		$cache = Framework_Cache::factory();

		
		
		$key = array('Cache'=>$handler->getCache(),'Group'=>$handler->getGroup());

		if (is_array($table)) {
			foreach($table AS $item) {
				self::saveLog($item, $key, $cache);
			}
		} else {
			self::saveLog($table, $key, $cache);
		}
	}

	/**
	 * saveLog
	 *
	 * @access  private
	 * @param	string		$table
	 * @param	string		$key	
	 * @param 	string		$cache
	 * @return  void
	 * @static
	 */
	static private function saveLog($table, $key, $cache) {
// data in the Dependencies group consists of
// ids that are table names, and per table 
// a data array/store that holds arrays with caches and groups as keys
// for example, a cacheable module X that relies on tablex's data would
// have a handler that supplies an array like this: array('Cache=>'X', 'Group' => 'default');,
// which would be stored in the tablex id
// this means that anytime that table gets an insert/update/delete, the cache X gets cleaned for the
// group 

// sometimes cachehandlers may return dynamic groups, such that you could have many entries for one
// module on a table with a different group. Group can usually be table ID's 
		
		
		$table = trim($table);
		if($data = $cache->get($table, 'Dependencies') ) {

			if(!in_array($key, $data)) {

				array_push($data, $key);

			}

		} else {
			$data = array($key);
		}

		$cache->save($data, $table, 'Dependencies');

	}
	
	/**
	 * cache
	 *
	 * Not to be confused with the other cache, this cache
	 * function just caches the db and table objects themselves
	 * for later use
	 * 
	 * @access  public
	 * @return  void
	 * @static
	 */
	static public function cache()
	{
		
		$cache = Framework_Cache::factory();
		
		foreach(self::$databases as $db => $instance) {
			if (!in_array($db, self::$dbCache)) {
				$cache->save($instance, $db, 'DB');
			}
		}
		
		foreach(self::$tables as $table => $instance) {
			if (!in_array($table, self::$tableCache)) {
				$cache->save($instance, $table, 'Table');
			}
		}
		
		
	}

	/**
	 * clearCache
	 *
	 * @access  public
	 * @param	string		$table
	 * @param	string		$group
	 * @return  object
	 * @static
	 */
	static public function clearCache($table, $group = null)
	{
		if (is_null($group)) {
			if (Framework::$request->ownerName) {
				$group = Framework::$request->ownerName;
			} else {
				$group = 'default';
			}
		} 
		
		// Start with the base cache
		$cache = Framework_Cache::factory();
		
		if($data = $cache->get($table, 'Dependencies') ) {
			if(is_array($data)) {
//				echo 'cleaning ';
//				var_dump($data);
				// $data is an array of arrays
				foreach( $data AS $cacheReference) {
					if ($cacheReference['Group'] == $group || $group == 'default' || $cacheReference['Group'] == 'default') {
						// the actual cache to clear
						$objectCache = Framework_Cache::factory($cacheReference['Cache']);
						$objectCache->clean($group);
					}
				}
			}

		}

	}
}

?>
